package gmmxEditor;

import gmmxEditor.GUI.ImageFilter;
import gmmxEditor.GUI.TilesPane;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Vector;

import javax.imageio.ImageIO;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.SwingConstants;
import javax.swing.border.MatteBorder;

import mh.entity.EnumState;
import mh.entity.object.EntityObject;
import mh.map.objectset.OSXImporter;

public class Main {

	/**
	 *********************** 
	 * CLASS VARIABLES *
	 *********************** 
	 */

	/**
	 * Main frame of the application
	 */
	private JFrame					frame;

	/**
	 * Map panel (top-left)
	 */
	private JPanel					mapPanel;

	/**
	 * Properties panel (top-right)
	 */
	private JPanel					propertiesPanel;

	/**
	 * Objects panel (middle-center)
	 */
	private TilesPane<SimpleRender>	objectsPanel;

	/**
	 * State panel (bottom)
	 */
	private JPanel					statePanel;

	/**
	 * Map image to be drawn
	 */
	private BufferedImage			map;

	/**
	 * Square to select in the mapPanel
	 */
	private Point					selectedPoint;

	/**
	 * Label where will be shown x and y positions
	 */
	private JLabel					positionLbl;

	/**
	 * Label where will be shown the id of the selected object
	 */
	private JLabel					objectIdLbl;

	/**
	 * File chooser window
	 */
	final JFileChooser				fc				= new JFileChooser();

	/**
	 * Matrix containing all the squares in the map with the id of the object inside
	 */
	private int[][]					matrix;

	/**
	 * Matrix containing all the states of the objects placed in the map
	 */
	private int[][]					states;

	/**
	 * Render of the selected object for painting on map
	 */
	private SimpleRender			selectedRender	= null;

	/**
	 * 
	 */
	private JPanel					renderView;

	private JComboBox<EnumState>	state;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			@Override
			public void run() {
				try {
					Main window = new Main();
					Preferences.setInstance(window);
					window.frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 */
	public Main() {
		this.initialize();
	}

	/**
	 * Initialize the contents of the frame.
	 */
	private void initialize() {
		this.frame = new JFrame();
		this.frame.setBounds(100, 100, 800, 750);
		this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.frame.setTitle("GMMX Editor");

		this.frame.setLocationRelativeTo(null);

		this.mapPanel = new MapPane();
		this.mapPanel.setBorder(null);
		this.mapPanel.setBackground(Color.WHITE);
		this.mapPanel.setLayout(null);

		this.mapPanel.addMouseMotionListener(new MouseMotionAdapter() {
			@Override
			public void mouseMoved(MouseEvent e) {//Mouse moved over map Panel
				if (Main.this.map == null) {
					return;
				}

				int x = (int) e.getPoint().getX();
				int y = (int) e.getPoint().getY();

				Point p = Main.this.getMapPoint(x, y);

				if (!p.equals(Main.this.selectedPoint)) {
					Main.this.setMapPosition(p, true);
				}
			}
		});

		this.mapPanel.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseExited(MouseEvent e) {//mouse moved away from map Panel
				Main.this.setMapPosition(null, true);
			}

			@Override
			public void mousePressed(MouseEvent e) {
				Main.this.putObject();
			}

		});

		JScrollPane scroller = new JScrollPane(this.mapPanel);
		scroller.setBounds(10, 32, 600, 500);

		this.frame.getContentPane().add(scroller);

		this.propertiesPanel = new JPanel();
		this.propertiesPanel.setBorder(new MatteBorder(1, 1, 0, 1, new Color(0, 0, 0)));
		this.propertiesPanel.setBounds(610, 32, 164, 500);
		this.frame.getContentPane().setLayout(null);
		this.frame.getContentPane().add(this.propertiesPanel);
		this.propertiesPanel.setLayout(null);

		this.renderView = new JPanel();
		this.renderView.setBounds(6, 6, 152, 175);
		this.propertiesPanel.add(this.renderView);

		JLabel lblState = new JLabel("State:");
		lblState.setBounds(6, 213, 35, 16);
		this.propertiesPanel.add(lblState);

		this.state = new JComboBox(EnumState.values());
		this.state.setBounds(53, 209, 105, 27);

		this.state.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				Main.this.repaintRenderView();
			}
		});

		this.propertiesPanel.add(this.state);

		JMenuBar menuBar = new JMenuBar();
		menuBar.setBounds(0, 0, 800, 21);
		this.frame.getContentPane().add(menuBar);

		JMenu mnFile = new JMenu("File");
		menuBar.add(mnFile);

		JMenuItem mntmNuovo = new JMenuItem("New");
		mnFile.add(mntmNuovo);

		JMenuItem mntmApri = new JMenuItem("Open");
		mnFile.add(mntmApri);

		JSeparator separator_1 = new JSeparator();
		mnFile.add(separator_1);

		JMenuItem mntmSave = new JMenuItem("Save");
		mntmSave.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				Main.this.save();
			}
		});
		mnFile.add(mntmSave);

		JMenu mnModifica = new JMenu("Edit");
		menuBar.add(mnModifica);

		JMenu mnAdd = new JMenu("Add tiles");
		mnModifica.add(mnAdd);

		JMenuItem mntmMap = new JMenuItem("Map");
		mntmMap.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {//choose map file
				Main.this.fc.setAcceptAllFileFilterUsed(false);
				Main.this.fc.removeChoosableFileFilter(null);
				Main.this.fc.setFileFilter(new ImageFilter());
				int returnVal = Main.this.fc.showOpenDialog(Main.this.frame);

				if (returnVal == JFileChooser.APPROVE_OPTION) {
					File file = Main.this.fc.getSelectedFile();

					try {
						Main.this.map = ImageIO.read(file);
						Main.this.mapPanel.setPreferredSize(new Dimension(Main.this.map.getWidth(), Main.this.map.getHeight()));

						Main.this.createMatrix();

						Main.this.mapPanel.revalidate();
						Main.this.mapPanel.repaint();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		});
		mnAdd.add(mntmMap);

		JMenuItem mntmObjects = new JMenuItem("Objects");
		mntmObjects.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {//import objects
				Main.this.fc.setAcceptAllFileFilterUsed(false);
				Main.this.fc.removeChoosableFileFilter(null);
				Main.this.fc.setFileFilter(new ImageFilter());
				int returnVal = Main.this.fc.showOpenDialog(Main.this.frame);

				if (returnVal == JFileChooser.APPROVE_OPTION) {
					File file = Main.this.fc.getSelectedFile();

					try {//check if the image is in OTX format
						BufferedImage img = ImageIO.read(file);

						if (!OSXImporter.isOSX(img)) {
							System.out.println("Contenuto file non in formato OTX");
						}

						OSXImporter importer = new OSXImporter(file.getPath()); //start import

						Vector<EntityObject> obj = importer.getObjects();
						Vector<SimpleRender> renders = new Vector<SimpleRender>();
						for (EntityObject o : obj) {
							renders.add(new SimpleRender(o.getRender()));
							//TODO replace with just render
						}

						Main.this.objectsPanel.addObjects(new Vector<SimpleRender>(renders));

						Main.this.redrawObjects();
					} catch (IOException e1) {
						e1.printStackTrace();
					}
				}
			}
		});
		mnAdd.add(mntmObjects);

		JSeparator separator = new JSeparator();
		mnModifica.add(separator);

		JMenuItem mntmPreferences = new JMenuItem("Preferences");
		mntmPreferences.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				new PreferencesPane(Main.this.frame);
			}
		});
		mnModifica.add(mntmPreferences);

		this.objectsPanel = new TilesPane<SimpleRender>();
		this.objectsPanel.setBackground(Color.WHITE);

		JScrollPane scroller_1 = new JScrollPane(this.objectsPanel);
		scroller_1.setBounds(10, 532, 764, 169);

		this.frame.getContentPane().add(scroller_1);

		this.objectsPanel.addMouseListener(new MouseAdapter() {
			@Override
			public void mousePressed(MouseEvent e) {
				SimpleRender render = Main.this.objectsPanel.getAt(e.getX(), e.getY());
				if (render != null) {
					Main.this.selectedRender = render;
					Main.this.repaintRenderView();
				}
			}
		});

		this.objectsPanel.addMouseMotionListener(new MouseMotionAdapter() {
			@Override
			public void mouseMoved(MouseEvent e) {//Mouse moved over objects Panel
				Main.this.setObjectsPosition(e.getPoint(), false);
			}
		});

		this.objectsPanel.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseExited(MouseEvent e) {//moused moved away from objects Panel
				Main.this.setObjectsPosition(null, true);
			}
		});

		this.statePanel = new JPanel();
		this.statePanel.setBounds(10, 701, 764, 21);
		this.frame.getContentPane().add(this.statePanel);
		this.statePanel.setLayout(null);

		this.positionLbl = new JLabel("(0:0)");
		this.positionLbl.setHorizontalAlignment(SwingConstants.CENTER);
		this.positionLbl.setBounds(664, 0, 100, 21);
		this.statePanel.add(this.positionLbl);

		this.objectIdLbl = new JLabel("(0)");
		this.objectIdLbl.setHorizontalAlignment(SwingConstants.CENTER);
		this.objectIdLbl.setBounds(615, 2, 61, 16);
		this.statePanel.add(this.objectIdLbl);
	}

	/**
	 * Panel that will draw the image (pattern used for scrolling scope)
	 */
	public class MapPane extends JPanel {

		private static final long	serialVersionUID	= 1L;

		/**
		 * Draws the map, if present, and all the lines over it
		 */
		@Override
		protected void paintComponent(Graphics g) {
			super.paintComponent(g);

			if (Main.this.map != null) {
				g.drawImage(Main.this.map, 0, 0, null);//map drawing

				g.setColor(Preferences.getInstance().getLinesColor());//setting color

				for (int i = 0; i < Main.this.map.getHeight(); i += Preferences.getInstance().getSquareSide()) {//horizontal drawing
					g.drawLine(0, i, Main.this.map.getWidth(), i);
				}

				for (int i = 0; i < Main.this.map.getWidth(); i += Preferences.getInstance().getSquareSide()) {//vertical drawing
					g.drawLine(i, 0, i, Main.this.map.getHeight());
				}

				g.setColor(Preferences.getInstance().getSquareColor());
				if (Main.this.selectedPoint != null) {
					g.fillRect((int) (Preferences.getInstance().getSquareSide() * Main.this.selectedPoint.getX()) + 1, (int) (Preferences.getInstance()
							.getSquareSide() * Main.this.selectedPoint.getY()) + 1, Preferences.getInstance().getSquareSide() - 1, Preferences.getInstance()
							.getSquareSide() - 1);
				}

				for (int y = 0; y < Main.this.matrix.length; y++) {
					for (int x = 0; x < Main.this.matrix.length; x++) {
						if (Main.this.matrix[y][x] != 0) {
							SimpleRender render = Main.this.objectsPanel.getByID(Main.this.matrix[y][x]);
							render.draw(g, EnumState.getState(Main.this.states[y][x]), ((y * Preferences.getInstance().getSquareSide()) - render.getSize()
									.getHeight()) + Preferences.getInstance().getSquareSide(), x * Preferences.getInstance().getSquareSide());
						}
						if ((Main.this.selectedPoint != null) && (Main.this.selectedPoint.getX() == x) && (Main.this.selectedPoint.getY() == y)) {
							if (Main.this.selectedRender != null) {
								Main.this.selectedRender.draw(g, (EnumState) Main.this.state.getSelectedItem(),
										(int) (Main.this.selectedPoint.getX() * Preferences.getInstance().getSquareSide()),
										(int) (((Main.this.selectedPoint.getY() * Preferences.getInstance().getSquareSide()) - Main.this.selectedRender
												.getSize().getHeight()) + Preferences.getInstance().getSquareSide()));
							}
						}
					}
				}

			}
		}
	}

	/**
	 * Returns the mouse x and y coords (map-converted) from the mouse
	 * 
	 * @param x
	 * @param y
	 * @return Point
	 */
	private Point getMapPoint(int x, int y) {
		return new Point(x / Preferences.getInstance().getSquareSide(), y / Preferences.getInstance().getSquareSide());
	}

	/**
	 * Sets position on mouse over map (draws text, sets square position, redraws the map)
	 * 
	 * @param p
	 * @param triggered
	 */
	private void setMapPosition(Point p, boolean triggered) {
		if (triggered) {
			this.setObjectsPosition(null, false);
		}

		if (p != null) {
			this.selectedPoint = p;
			this.mapPanel.repaint();
			this.positionLbl.setText("(" + ((int) p.getX() + 1) + ":" + ((int) p.getY() + 1) + ")");
		} else {
			this.selectedPoint = null;
			this.mapPanel.repaint();
			this.positionLbl.setText("(0:0)");
		}
	}

	/**
	 * Sets position on mouse over objects (sets square position, redraws the panel)
	 * 
	 * @param p
	 * @param triggered
	 */
	private void setObjectsPosition(Point p, boolean triggered) {
		if (triggered) {
			this.setMapPosition(null, false);
		}

		this.objectsPanel.setPointer(p);

		if ((p != null) && (this.objectsPanel.elements() > 0) && (this.objectsPanel.getSelectedObject() != null)) {
			this.objectIdLbl.setText("(" + this.objectsPanel.getSelectedObject().getEntityDatabaseID() + ")");
		} else {
			this.objectIdLbl.setText("(0)");
		}

		this.objectsPanel.repaint();
	}

	/**
	 * Redraws the map
	 */
	public void repaintMap() {
		this.mapPanel.repaint();
	}

	/**
	 * Redraws the objects
	 */
	public void redrawObjects() {
		this.objectsPanel.repaint();
	}

	private void save() {
		//TODO save everything to file
		System.out.println("LINE: " + Converter.encode(this.matrix));
	}

	public void createMatrix() {
		this.matrix = new int[this.map.getWidth() / Preferences.getInstance().getSquareSide()][this.map.getHeight() / Preferences.getInstance().getSquareSide()];
		this.states = new int[this.map.getWidth() / Preferences.getInstance().getSquareSide()][this.map.getHeight() / Preferences.getInstance().getSquareSide()];

		for (int i = 0; i < this.matrix.length; i++) {
			for (int j = 0; j < this.matrix[i].length; j++) {
				this.matrix[i][j] = 0;
				this.states[i][j] = 0;
			}
		}

		System.out.println("Matrix (" + this.matrix.length + "x" + this.matrix[0].length + ") created");
	}

	public void repaintRenderView() {
		//TODO add animations
		if (this.selectedRender != null) {
			Graphics g = this.renderView.getGraphics();

			g.clearRect(0, 0, this.renderView.getWidth(), this.renderView.getHeight());
			this.selectedRender.draw(g, (EnumState) (this.state.getSelectedItem()), (this.renderView.getWidth() / 2)
					- (this.selectedRender.getSize().getWidth() / 2), (this.renderView.getHeight() / 2) - (this.selectedRender.getSize().getHeight() / 2));
		}
	}

	private void putObject() {
		if ((this.selectedRender == null) || (this.selectedPoint == null)) {
			return;
		}

		this.matrix[(int) this.selectedPoint.getX()][(int) this.selectedPoint.getY()] = this.selectedRender.getEntityDatabaseID();
		this.states[(int) this.selectedPoint.getX()][(int) this.selectedPoint.getY()] = ((EnumState) this.state.getSelectedItem()).toInt();
		this.mapPanel.repaint();
	}

}
